package com.ncmem.up6.store.aliyun;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import com.ncmem.up6.model.FileInf;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.List;

public class OSSTool {


    /**
     * API：
     * 	https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/API/API_PutObject.html
     * @param key 文件名称,QQ.exe
     * @param data
     */
    public static String putObject(String key,byte[] data) throws Exception{

        OSSConfig cfg = new OSSConfig();
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(cfg.endpoint, cfg.ak, cfg.sk);

        try {
            // 填写字符串。

            // 创建PutObjectRequest对象。
            PutObjectRequest req = new PutObjectRequest(cfg.bucket, key, new ByteArrayInputStream(data));

            // 如果需要上传时设置存储类型和访问权限，请参考以下示例代码。
            // ObjectMetadata metadata = new ObjectMetadata();
            // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
            // metadata.setObjectAcl(CannedAccessControlList.Private);
            // putObjectRequest.setMetadata(metadata);

            // 上传字符串。
            ossClient.putObject(req);
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return "";
    }

    /**
     * API：
     * 	https://help.aliyun.com/document_detail/84825.html
     * @param key 以左斜杠开始的资源路径。/guid/QQ.exe径
     * @param blockOffset
     * @param blockLen
     * @return
     */
    public static byte[] getObject(String key,long blockOffset,long blockLen)throws Exception
    {
        OSSConfig cfg = new OSSConfig();
        OSS client = new OSSClientBuilder().build(cfg.endpoint,cfg.ak,cfg.sk);
        InputStream in = null;
        ByteArrayOutputStream baos =new ByteArrayOutputStream();
        try {
            GetObjectRequest getObjectRequest = new GetObjectRequest(cfg.bucket, key);
            // 对于大小为1000 Bytes的文件，正常的字节范围为0~999。
            // 获取0~999字节范围内的数据，包括0和999，共1000个字节的数据。如果指定的范围无效（比如开始或结束位置的指定值为负数，或指定值大于文件大小），则下载整个文件。
            getObjectRequest.setRange(blockOffset,blockOffset+blockLen-1);

            // 范围下载。
            OSSObject ossObject = client.getObject(getObjectRequest);

            // 读取数据。
            byte[] buf = new byte[1024];
            in = ossObject.getObjectContent();
            for (int n = 0; n != -1&&blockLen>0; ) {
                n = in.read(buf, 0, buf.length);
                blockLen-= n;
                baos.write(buf,0,n);
            }
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (client != null) {
                client.shutdown();
            }
            // 数据读取完成后，获取的流必须关闭，否则会造成连接泄漏，导致请求无连接可用，程序无法正常工作。
            if (in != null) {
                in.close();
            }
        }
        return baos.toByteArray();
    }

    /**
     * API：
     * 	https://help.aliyun.com/document_detail/84786.html
     * @param key guid/QQ.exe，
     * @return
     */
    public static String CreateMultipartUpload(String key) {
        OSSConfig cfg = new OSSConfig();
        OSS client = new OSSClientBuilder().build(cfg.endpoint,cfg.ak,cfg.sk);
        // 创建InitiateMultipartUploadRequest对象。
        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(cfg.bucket, key);
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(MediaTypeFactory.getMediaType(key).orElse(MediaType.APPLICATION_OCTET_STREAM).toString());
        request.setObjectMetadata(metadata);
        // 初始化分片。
        InitiateMultipartUploadResult upresult = client.initiateMultipartUpload(request);
        // 返回uploadId，它是分片上传事件的唯一标识。您可以根据该uploadId发起相关的操作，例如取消分片上传、查询分片上传等。
        String uploadId = upresult.getUploadId();
        return  uploadId;
    }

    /**
     * API:
     * 	https://help.aliyun.com/document_detail/84786.html
     * @param key /guid/QQ.exe
     * @param data
     */
    public static String UploadPart(String key, int part,String uploadID, byte[] data) {
        OSSConfig cfg = new OSSConfig();
        OSS client = new OSSClientBuilder().build(cfg.endpoint,cfg.ak,cfg.sk);
        UploadPartRequest uppart = new UploadPartRequest();
        uppart.setBucketName(cfg.bucket);
        uppart.setKey(key);
        uppart.setUploadId(uploadID);
        uppart.setPartNumber(part);//基于1
        uppart.setPartSize(data.length);//
        ByteArrayInputStream bis = new ByteArrayInputStream(data);
        uppart.setInputStream(bis);

        // 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
        UploadPartResult uploadPartResult = client.uploadPart(uppart);
        // 每次上传分片之后，OSS的返回结果包含PartETag。PartETag将被保存在partETags中。
        return uploadPartResult.getPartETag().getETag();
    }

    /**
     * 参数：pathSvr,minio_id(UploadId)
     * API:
     * 	https://help.aliyun.com/document_detail/84786.html
     * POST /Key+?uploadId=UploadId HTTP/1.1
     Host: Bucket.s3.amazonaws.com
     <?xml version="1.0" encoding="UTF-8"?>
     <CompleteMultipartUpload xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
     <Part>
     <ChecksumCRC32>string</ChecksumCRC32>
     <ChecksumCRC32C>string</ChecksumCRC32C>
     <ChecksumSHA1>string</ChecksumSHA1>
     <ChecksumSHA256>string</ChecksumSHA256>
     <ETag>string</ETag>
     <PartNumber>integer</PartNumber>
     </Part>
     ...
     </CompleteMultipartUpload>

     * @param file
     */
    public static boolean CompleteMultipartUpload(FileInf file)
    {
        OSSConfig cfg = new OSSConfig();
        OSS client = new OSSClientBuilder().build(cfg.endpoint,cfg.ak,cfg.sk);

        List<PartETag> etags =file.etagsOSS();

        CompleteMultipartUploadRequest cmur =
                new CompleteMultipartUploadRequest(cfg.bucket, file.ObsKey(), file.object_id, etags);

        // 完成分片上传。
        CompleteMultipartUploadResult completeMultipartUploadResult = client.completeMultipartUpload(cmur);
        System.out.println(completeMultipartUploadResult.getETag());
        return true;
    }
}
